
Anh Tuan
Data Science Expert

Untuk otomatisasi web, Browser4 (dari PulsarRPA) telah muncul sebagai mesin browser yang cepat, aman korutin, yang dirancang untuk ekstraksi data berkekuatan AI. Dengan kemampuan yang mendukung 100k-200k kunjungan halaman kompleks per mesin per hari, Browser4 dirancang untuk skala yang serius. Namun, ketika mengekstrak data dari situs web yang dilindungi, tantangan CAPTCHA menjadi penghalang yang signifikan.
CapSolver memberikan pelengkap yang sempurna untuk kemampuan otomatisasi Browser4, memungkinkan agen Anda untuk menavigasi halaman yang dilindungi CAPTCHA secara mulus. Integrasi ini menggabungkan otomatisasi browser berkecepatan tinggi Browser4 dengan solusi CAPTCHA yang unggul di industri.
Browser4 adalah kerangka kerja otomatisasi browser berkinerja tinggi yang dibangun dalam Kotlin. Dirancang untuk aplikasi AI yang memerlukan kemampuan agen mandiri, throughput ekstrem, dan ekstraksi data hibrid yang menggabungkan LLM, algoritma machine learning, dan pendekatan berbasis selector.
| Metode | Deskripsi |
|---|---|
session.open(url) |
Memuat halaman dan mengembalikan PageSnapshot |
session.parse(page) |
Mengonversi snapshot menjadi dokumen di memori |
driver.selectFirstTextOrNull(selector) |
Mengambil teks dari DOM yang sedang berjalan |
driver.evaluate(script) |
Menjalankan JavaScript di browser |
session.extract(document, fieldMap) |
Memetakan selector CSS ke bidang yang terstruktur |
CapSolver adalah layanan penyelesaian CAPTCHA yang terkemuka yang menyediakan solusi berkekuatan AI untuk melewati berbagai tantangan CAPTCHA. Dengan dukungan untuk berbagai jenis CAPTCHA dan respons yang cepat, CapSolver terintegrasi secara mulus ke dalam alur kerja otomatis.
Ketika membangun otomatisasi Browser4 yang berinteraksi dengan situs web yang dilindungi—baik untuk ekstraksi data, pemantauan harga, atau penelitian pasar—tantangan CAPTCHA menjadi penghalang yang signifikan. Berikut adalah alasan mengapa integrasi ini penting:

Maven (pom.xml):
<dependencies>
<!-- Browser4/PulsarRPA -->
<dependency>
<groupId>ai.platon.pulsar</groupId>
<artifactId>pulsar-boot</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Klien HTTP untuk CapSolver -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Pemrosesan JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- Korutin Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
Gradle (build.gradle.kts):
dependencies {
implementation("ai.platon.pulsar:pulsar-boot:2.2.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}
Buat file application.properties:
# Konfigurasi CapSolver
CAPSOLVER_API_KEY=kunci_api_capsolver_anda
# Konfigurasi LLM (opsional, untuk ekstraksi AI)
OPENROUTER_API_KEY=kunci_api_openrouter_anda
# Konfigurasi Proxy (opsional)
PROXY_ROTATION_URL=url_proxy_anda
Berikut adalah layanan Kotlin yang dapat digunakan kembali yang mengintegrasikan CapSolver dengan Browser4:
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import kotlinx.coroutines.delay
import java.util.concurrent.TimeUnit
data class TaskResult(
val gRecaptchaResponse: String? = null,
val token: String? = null,
val cookies: List<Map<String, String>>? = null,
val userAgent: String? = null
)
class CapSolverService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://api.capsolver.com"
private val jsonMediaType = "application/json".toMediaType()
private suspend fun createTask(taskData: Map<String, Any>): String {
val payload = mapOf(
"clientKey" to apiKey,
"task" to taskData
)
val request = Request.Builder()
.url("$baseUrl/createTask")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
if (result.get("errorId").asInt != 0) {
throw Exception("Kesalahan CapSolver: ${result.get("errorDescription").asString}")
}
return result.get("taskId").asString
}
private suspend fun getTaskResult(taskId: String, maxAttempts: Int = 60): TaskResult {
val payload = mapOf(
"clientKey" to apiKey,
"taskId" to taskId
)
repeat(maxAttempts) {
delay(2000)
val request = Request.Builder()
.url("$baseUrl/getTaskResult")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
when (result.get("status")?.asString) {
"ready" -> {
val solution = result.getAsJsonObject("solution")
return TaskResult(
gRecaptchaResponse = solution.get("gRecaptchaResponse")?.asString,
token = solution.get("token")?.asString,
userAgent = solution.get("userAgent")?.asString
)
}
"failed" -> throw Exception("Tugas gagal: ${result.get("errorDescription")?.asString}")
}
}
throw Exception("Waktu tunggu solusi CAPTCHA habis")
}
suspend fun solveReCaptchaV2(websiteUrl: String, websiteKey: String): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV2TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Tidak ada gRecaptchaResponse dalam solusi")
}
suspend fun solveReCaptchaV3(
websiteUrl: String,
websiteKey: String,
pageAction: String = "submit"
): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV3TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey,
"pageAction" to pageAction
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Tidak ada gRecaptchaResponse dalam solusi")
}
suspend fun solveTurnstile(
websiteUrl: String,
websiteKey: String,
action: String? = null,
cdata: String? = null
): String {
val taskData = mutableMapOf(
"type" to "AntiTurnstileTaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
)
// Tambahkan metadata opsional
if (action != null || cdata != null) {
val metadata = mutableMapOf<String, String>()
action?.let { metadata["action"] = it }
cdata?.let { metadata["cdata"] = it }
taskData["metadata"] = metadata
}
val taskId = createTask(taskData)
val result = getTaskResult(taskId)
return result.token ?: throw Exception("Tidak ada token dalam solusi")
}
suspend fun checkBalance(): Double {
val payload = mapOf("clientKey" to apiKey)
val request = Request.Builder()
.url("$baseUrl/getBalance")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.get("balance")?.asDouble ?: 0.0
}
}
import ai.platon.pulsar.context.PulsarContexts
import ai.platon.pulsar.skeleton.session.PulsarSession
import kotlinx.coroutines.runBlocking
class ReCaptchaV2Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptcha(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Menyelesaikan reCAPTCHA v2...")
// Selesaikan CAPTCHA terlebih dahulu
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
println("CAPTCHA diselesaikan, panjang token: ${token.length}")
// Buat sesi dan buka halaman
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Sisipkan token ke textarea tersembunyi menggunakan properti value (aman)
driver?.evaluate("""
(function() {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = arguments[0];
})('$token');
""")
// Kirim formulir
driver?.evaluate("document.querySelector('form').submit();")
// Tunggu navigasi
Thread.sleep(3000)
// Ekstrak data dari halaman hasil
val document = session.parse(page)
mapOf(
"title" to document.selectFirstTextOrNull("h1"),
"content" to document.selectFirstTextOrNull(".content"),
"success" to (document.body().text().contains("success", ignoreCase = true))
)
}
}
fun main() = runBlocking {
val apiKey = System.getenv("CAPSOLVER_API_KEY") ?: "kunci_api_anda"
val capSolver = CapSolverService(apiKey)
val extractor = ReCaptchaV2Extractor(capSolver)
val result = extractor.extractWithCaptcha(
targetUrl = "https://example.com/halaman-terlindungi",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC"
)
println("Hasil ekstraksi: $result")
}
class ReCaptchaV3Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptchaV3(
targetUrl: String,
siteKey: String,
action: String = "submit"
): Map<String, Any?> {
println("Menyelesaikan reCAPTCHA v3 dengan aksi: $action")
// Selesaikan reCAPTCHA v3 dengan aksi halaman khusus
val token = capSolver.solveReCaptchaV3(
websiteUrl = targetUrl,
websiteKey = siteKey,
pageAction = action
)
println("Token diperoleh secara sukses")
// Buat sesi dan buka halaman
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Sisipkan token ke input tersembunyi (menggunakan penugasan nilai yang aman)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="g-recaptcha-response"]');
if (input) {
input.value = tokenValue;
} else {
var hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'g-recaptcha-response';
hidden.value = tokenValue;
var form = document.querySelector('form');
if (form) form.appendChild(hidden);
}
})('$token');
""")
// Klik tombol submit
driver?.evaluate("document.querySelector('#submit-btn').click();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"result" to document.selectFirstTextOrNull(".result-data"),
"status" to "success"
)
}
}
class TurnstileExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithTurnstile(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Menyelesaikan Cloudflare Turnstile...")
// Selesaikan dengan metadata opsional (aksi dan cdata)
val token = capSolver.solveTurnstile(
targetUrl,
siteKey,
action = "login", // opsional
cdata = "0000-1111-2222-3333-example" // opsional
)
println("Turnstile berhasil diselesaikan!")
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Menyuntikkan token Turnstile (menggunakan penugasan nilai yang aman)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="cf-turnstile-response"]');
if (input) input.value = tokenValue;
})('$token');
""")
// Kirim
driver?.evaluate("document.querySelector('form').submit();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"title" to document.selectFirstTextOrNull("title"),
"content" to document.selectFirstTextOrNull("body")?.take(500)
)
}
}
---
## Integrasi dengan Browser4 X-SQL
Browser4's X-SQL menawarkan kemampuan ekstraksi yang kuat. Berikut cara menggabungkannya dengan penyelesaian CAPTCHA:
```kotlin
class XSqlCaptchaExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractProductsWithCaptcha(
targetUrl: String,
siteKey: String
): List<Map<String, Any?>> {
// Selesaikan CAPTCHA terlebih dahulu
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
// Membuat sesi dan membangun sesi yang terautentikasi
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
document.querySelector('form').submit();
})('$token');
""")
Thread.sleep(3000)
// Sekarang parsing halaman dan ekstrak data produk
val document = session.parse(page)
// Ekstrak data produk menggunakan metode sesi bawaan
val products = mutableListOf<Map<String, Any?>>()
val productElements = document.select(".product-item")
for ((index, element) in productElements.withIndex()) {
if (index >= 50) break // BATAS 50
products.add(mapOf(
"name" to element.selectFirstTextOrNull(".product-name"),
"price" to element.selectFirstTextOrNull(".price")?.let {
"""(\d+\.?\d*)""".toRegex().find(it)?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0
},
"rating" to element.selectFirstTextOrNull(".rating")
))
}
return products.map { row ->
mapOf(
"name" to row["name"],
"price" to row["price"],
"rating" to row["rating"],
"image_url" to row["image_url"]
)
}
}
}
Untuk situs yang memerlukan CAPTCHA sebelum mengakses konten, gunakan alur kerja pra-autentikasi:
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
class PreAuthenticator(
private val capSolver: CapSolverService
) {
data class AuthSession(
val cookies: Map<String, String>,
val userAgent: String?
)
suspend fun authenticateWithCaptcha(
loginUrl: String,
siteKey: String
): AuthSession {
// Selesaikan CAPTCHA
val captchaToken = capSolver.solveReCaptchaV2(loginUrl, siteKey)
// Kirim CAPTCHA untuk mendapatkan cookie sesi
val client = OkHttpClient.Builder()
.cookieJar(object : CookieJar {
private val cookies = mutableListOf<Cookie>()
override fun saveFromResponse(url: HttpUrl, cookieList: List<Cookie>) {
cookies.addAll(cookieList)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> = cookies
})
.build()
val formBody = okhttp3.FormBody.Builder()
.add("g-recaptcha-response", captchaToken)
.build()
val request = Request.Builder()
.url(loginUrl)
.post(formBody)
.build()
val response = client.newCall(request).execute()
// Ekstrak cookie dari respons
val responseCookies = response.headers("Set-Cookie")
.associate { cookie ->
val parts = cookie.split(";")[0].split("=", limit = 2)
parts[0] to (parts.getOrNull(1) ?: "")
}
return AuthSession(
cookies = responseCookies,
userAgent = response.request.header("User-Agent")
)
}
}
class AuthenticatedExtractor(
private val preAuth: PreAuthenticator,
private val capSolver: CapSolverService
) {
suspend fun extractWithAuth(
loginUrl: String,
targetUrl: String,
siteKey: String
): Map<String, Any?> {
// Pra-autentikasi
val authSession = preAuth.authenticateWithCaptcha(loginUrl, siteKey)
println("Sesi berhasil dibuat dengan ${authSession.cookies.size} cookie")
// Membuat sesi Browser4
val session = PulsarContexts.createSession()
// Konfigurasi sesi dengan cookie
val cookieScript = authSession.cookies.entries.joinToString(";") { (k, v) ->
"$k=$v"
}
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Set cookie
driver?.evaluate("document.cookie = '$cookieScript';")
// Muat ulang dengan sesi yang terautentikasi
driver?.evaluate("location.reload();")
Thread.sleep(2000)
// Ekstrak data
val document = session.parse(page)
return mapOf(
"authenticated" to true,
"content" to document.selectFirstTextOrNull(".protected-content"),
"userData" to document.selectFirstTextOrNull(".user-profile")
)
}
}
Kemampuan AI Browser4 dapat ditingkatkan dengan OpenRouter, gateway API yang terpadu untuk mengakses berbagai model LLM. Ini memungkinkan ekstraksi konten yang cerdas yang menyesuaikan diri dengan struktur halaman yang berbeda.
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit
data class ChatMessage(val role: String, val content: String)
data class ChatCompletion(val content: String, val model: String, val usage: TokenUsage)
data class TokenUsage(val promptTokens: Int, val completionTokens: Int, val totalTokens: Int)
class OpenRouterService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://openrouter.ai/api/v1"
private val jsonMediaType = "application/json".toMediaType()
fun chat(
messages: List<ChatMessage>,
model: String = "openai/gpt-4o-mini"
): ChatCompletion {
val payload = mapOf(
"model" to model,
"messages" to messages.map { mapOf("role" to it.role, "content" to it.content) }
)
val request = Request.Builder()
.url("$baseUrl/chat/completions")
.header("Authorization", "Bearer $apiKey")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
val choice = result.getAsJsonArray("choices")?.get(0)?.asJsonObject
val content = choice?.getAsJsonObject("message")?.get("content")?.asString ?: ""
val usage = result.getAsJsonObject("usage")
return ChatCompletion(
content = content,
model = result.get("model")?.asString ?: model,
usage = TokenUsage(
promptTokens = usage?.get("prompt_tokens")?.asInt ?: 0,
completionTokens = usage?.get("completion_tokens")?.asInt ?: 0,
totalTokens = usage?.get("total_tokens")?.asInt ?: 0
)
)
}
fun extractStructuredData(html: String, schema: String): String {
val prompt = """
Ekstrak data berikut dari konten HTML ini.
Kembalikan HANYA JSON yang valid sesuai skema ini: $schema
HTML:
${html.take(4000)}
""".trimIndent()
return chat(listOf(ChatMessage("user", prompt))).content
}
fun listModels(): List<String> {
val request = Request.Builder()
.url("$baseUrl/models")
.header("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.getAsJsonArray("data")?.mapNotNull {
it.asJsonObject.get("id")?.asString
} ?: emptyList()
}
}
Gabungkan penyelesaian CAPTCHA dengan ekstraksi konten yang cerdas:
class SmartExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAI(
targetUrl: String,
siteKey: String?,
extractionPrompt: String
): Map<String, Any?> {
// Langkah 1: Selesaikan CAPTCHA jika diperlukan
val captchaToken = siteKey?.let {
println("Menyelesaikan CAPTCHA...")
capSolver.solveReCaptchaV2(targetUrl, it)
}
// Langkah 2: Membuat sesi dan membuka halaman
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
captchaToken?.let { token ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
var form = document.querySelector('form');
if (form) form.submit();
})('$token');
""")
Thread.sleep(3000)
}
// Langkah 3: Ekstrak konten halaman
val document = session.parse(page)
val pageContent = document.body().text().take(8000)
// Langkah 4: Gunakan LLM untuk ekstraksi data terstruktur
val llmResponse = openRouter.chat(listOf(
ChatMessage("system", "Anda adalah asisten ekstraksi data. Ekstrak data terstruktur dari halaman web."),
ChatMessage("user", """
$extractionPrompt
Konten halaman:
$pageContent
""".trimIndent())
))
println("LLM menggunakan ${llmResponse.usage.totalTokens} token")
return mapOf(
"url" to targetUrl,
"captchaSolved" to (captchaToken != null),
"extractedData" to llmResponse.content,
"tokensUsed" to llmResponse.usage.totalTokens
)
}
}
// Penggunaan
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val openRouter = OpenRouterService(System.getenv("OPENROUTER_API_KEY")!!)
val extractor = SmartExtractor(capSolver, openRouter)
val result = extractor.extractWithAI(
targetUrl = "https://example.com/products",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC",
extractionPrompt = """
Ekstrak semua produk dengan:
- nama
- harga (sebagai angka)
- ketersediaan (in_stock/out_of_stock)
- rating (1-5)
Kembalikan sebagai array JSON.
""".trimIndent()
)
println("Hasil ekstraksi: ${result["extractedData"]}")
}
Gunakan LLM untuk menghasilkan selektor CSS untuk struktur halaman yang tidak dikenal:
class AdaptiveExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAdaptiveSelectors(
targetUrl: String,
siteKey: String?,
dataFields: List<String>
): Map<String, Any?> {
// Selesaikan CAPTCHA terlebih dahulu
val token = siteKey?.let { capSolver.solveReCaptchaV2(targetUrl, it) }
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
token?.let { t ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
// Dapatkan struktur HTML halaman
val htmlSample = driver?.evaluate("document.body.innerHTML")?.toString()?.take(5000) ?: ""
// Minta LLM untuk menghasilkan selektor
val selectorPrompt = """
Analisis HTML ini dan berikan selektor CSS untuk bidang berikut: ${dataFields.joinToString(", ")}
Contoh HTML:
$htmlSample
Kembalikan JSON seperti: {"fieldName": "css-selector", ...}
""".trimIndent()
val selectorsJson = openRouter.chat(listOf(ChatMessage("user", selectorPrompt))).content
val selectors = Gson().fromJson(selectorsJson, Map::class.java) as Map<String, String>
// Ekstrak menggunakan selektor yang dihasilkan
val document = session.parse(page)
val extractedData = selectors.mapValues { (_, selector) ->
document.selectFirstTextOrNull(selector)
}
return mapOf(
"url" to targetUrl,
"selectors" to selectors,
"data" to extractedData
)
}
}
Desain Browser4 yang selaras dengan korelasi memungkinkan penyelesaian CAPTCHA yang efisien secara paralel:
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
data class ExtractionJob(
val url: String,
val siteKey: String?
)
data class ExtractionResult(
val url: String,
val data: Map<String, Any?>?,
val captchaSolved: Boolean,
val error: String?,
val duration: Long
)
class ParallelExtractor(
private val capSolver: CapSolverService,
private val concurrency: Int = 5
) {
suspend fun extractAll(jobs: List<ExtractionJob>): List<ExtractionResult> = coroutineScope {
val channel = Channel<ExtractionJob>(Channel.UNLIMITED)
val results = mutableListOf<ExtractionResult>()
// Kirim semua pekerjaan ke channel
jobs.forEach { channel.send(it) }
channel.close()
// Proses dengan konkurensi terbatas
val workers = (1..concurrency).map { workerId ->
async {
val workerResults = mutableListOf<ExtractionResult>()
// Setiap pekerja membuat sesi sendiri untuk keamanan thread
val workerSession = PulsarContexts.createSession()
for (job in channel) {
val startTime = System.currentTimeMillis()
var captchaSolved = false
try {
// Selesaikan CAPTCHA jika kunci situs diberikan
val token = job.siteKey?.let {
captchaSolved = true
capSolver.solveReCaptchaV2(job.url, it)
}
// Ekstrak data
val page = workerSession.open(job.url)
token?.let { t ->
val driver = workerSession.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
val document = workerSession.parse(page)
workerResults.add(ExtractionResult(
url = job.url,
data = mapOf(
"title" to document.selectFirstTextOrNull("title"),
"h1" to document.selectFirstTextOrNull("h1")
),
captchaSolved = captchaSolved,
error = null,
duration = System.currentTimeMillis() - startTime
))
} catch (e: Exception) {
workerResults.add(ExtractionResult(
url = job.url,
data = null,
captchaSolved = captchaSolved,
error = e.message,
duration = System.currentTimeMillis() - startTime
))
}
}
workerResults
}
}
workers.awaitAll().flatten()
}
}
// Penggunaan
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val extractor = ParallelExtractor(capSolver, concurrency = 5)
val jobs = listOf(
ExtractionJob("https://site1.com/data", "6Lc..."),
ExtractionJob("https://site2.com/data", null),
ExtractionJob("https://site3.com/data", "6Lc..."),
)
val results = extractor.extractAll(jobs)
val solved = results.count { it.captchaSolved }
println("Selesai ${results.size} ekstraksi, selesaikan $solved CAPTCHA")
results.forEach { r ->
println("${r.url}: ${r.duration}ms - ${r.error ?: "berhasil"}")
}
}
suspend fun <T> withRetry(
maxRetries: Int = 3,
initialDelay: Long = 1000,
block: suspend () -> T
): T {
var lastException: Exception? = null
repeat(maxRetries) { attempt ->
try {
return block()
} catch (e: Exception) {
lastException = e
println("Percobaan ${attempt + 1} gagal: ${e.message}")
delay(initialDelay * (attempt + 1))
}
}
throw lastException ?: Exception("Jumlah pengulangan maksimum tercapai")
}
// Penggunaan
val token = withRetry(maxRetries = 3) {
capSolver.solveReCaptchaV2(url, siteKey)
}
suspend fun ensureSufficientBalance(
capSolver: CapSolverService,
minBalance: Double = 1.0
) {
val balance = capSolver.checkBalance()
if (balance < minBalance) {
throw Exception("Saldo CapSolver tidak cukup: $${"%.2f".format(balance)}. Silakan isi ulang.")
}
println("Saldo CapSolver: $${"%.2f".format(balance)}")
}
class TokenCache(private val ttlMs: Long = 90_000) {
private data class CachedToken(val token: String, val timestamp: Long)
private val cache = mutableMapOf<String, CachedToken>()
private fun getKey(domain: String, siteKey: String) = "$domain:$siteKey"
fun get(domain: String, siteKey: String): String? {
val key = getKey(domain, siteKey)
val cached = cache[key] ?: return null
if (System.currentTimeMillis() - cached.timestamp > ttlMs) {
cache.remove(key)
return null
}
return cached.token
}
fun set(domain: String, siteKey: String, token: String) {
val key = getKey(domain, siteKey)
cache[key] = CachedToken(token, System.currentTimeMillis())
}
}
// Penggunaan dengan cache
class CachedCapSolver(
private val capSolver: CapSolverService,
private val cache: TokenCache = TokenCache()
) {
suspend fun solveReCaptchaV2Cached(websiteUrl: String, websiteKey: String): String {
val domain = java.net.URL(websiteUrl).host
cache.get(domain, websiteKey)?.let {
println("Menggunakan token yang dicache")
return it
}
val token = capSolver.solveReCaptchaV2(websiteUrl, websiteKey)
cache.set(domain, websiteKey, token)
return token
}
}
| Pengaturan | Deskripsi | Nilai Default |
|---|---|---|
CAPSOLVER_API_KEY |
Kunci API CapSolver Anda | - |
OPENROUTER_API_KEY |
Kunci API OpenRouter untuk fitur LLM | - |
PROXY_ROTATION_URL |
URL layanan rotasi proxy | - |
Browser4 menggunakan application.properties untuk konfigurasi tambahan |
Mengintegrasikan CapSolver dengan Browser4 menciptakan kombinasi yang kuat untuk ekstraksi data web berkecepatan tinggi. Arsitektur Browser4 yang aman korutin dan kemampuan kinerja ekstremnya, dikombinasikan dengan penyelesaian CAPTCHA yang andal dari CapSolver, memungkinkan ekstraksi data dalam skala besar.
Pola integrasi kunci:
Baik Anda membangun sistem pemantauan harga, alur pipa penelitian pasar, atau platform agregasi data, kombinasi Browser4 + CapSolver menyediakan keandalan dan skalabilitas yang diperlukan untuk lingkungan produksi.
Siap memulai? Daftar ke CapSolver dan gunakan kode bonus BROWSER4 untuk bonus tambahan 6% pada recharge pertama Anda!
Browser4 adalah kerangka kerja otomasi browser berkinerja tinggi dari PulsarRPA yang aman korutin. Dibangun dalam Kotlin dan dirancang untuk ekstraksi data berbasis AI, mendukung 100.000-200.000 kunjungan halaman kompleks per hari per mesin.
CapSolver terintegrasi dengan Browser4 melalui kelas layanan yang menyelesaikan CAPTCHA via API CapSolver. Token yang diselesaikan kemudian disisipkan ke halaman menggunakan kemampuan evaluasi JavaScript Browser4 (driver.evaluate()).
CapSolver mendukung reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge (5 detik), AWS WAF, GeeTest v3/v4, dan banyak lagi.
CapSolver menawarkan harga kompetitif berdasarkan jenis dan volume CAPTCHA yang diselesaikan. Kunjungi capsolver.com untuk harga terkini. Gunakan kode BROWSER4 untuk bonus 6%.
Browser4 dibangun dalam Kotlin dan berjalan di JVM (Java 17+). Browser4 juga bisa digunakan dari aplikasi Java.
Ya! Desain Browser4 yang aman korutin memungkinkan pemrosesan paralel yang efisien. Dengan API CapSolver, Anda bisa menyelesaikan beberapa CAPTCHA secara bersamaan di berbagai pekerjaan ekstraksi.
Kunci situs biasanya ditemukan dalam sumber HTML halaman:
data-sitekey pada elemen .g-recaptchadata-sitekey pada elemen .cf-turnstilePelajari arsitektur pengambilan data web Rust yang dapat diskalakan dengan reqwest, scraper, pengambilan data asinkron, pengambilan data browser tanpa tampilan, rotasi proxy, dan penanganan CAPTCHA yang sesuai aturan.

Mengotomasi penyelesaian CAPTCHA dengan Nanobot dan CapSolver. Gunakan Playwright untuk menyelesaikan reCAPTCHA dan Cloudflare secara otomatis.
